package tools

var (
	IntNotZero = func(value int) bool { return value == 0 }
	IntUnique  = func(value int) int { return value }
)

func SliceToMap[K comparable, T any](array []T, unique func(T) K) map[K]T {
	if len(array) == 0 {
		return nil
	}

	arrMap := make(map[K]T, len(array))
	for _, item := range array {
		key := unique(item)
		if _, exist := arrMap[key]; exist {
			continue
		}
		arrMap[key] = item
	}
	return arrMap
}

func SliceToMapAndCast[K comparable, T, C any](array []T, keyCast func(T) K, valueCast func(T) C) map[K]C {
	if len(array) == 0 {
		return nil
	}
	arrMap := make(map[K]C, len(array))
	for _, item := range array {
		key := keyCast(item)
		if _, exist := arrMap[key]; exist {
			continue
		}
		arrMap[key] = valueCast(item)
	}
	return arrMap
}

func SliceToMapFilterAndCast[K comparable, T, C any](array []T, keyCast func(T) K, filter func(T) bool, valueCast func(T) C) map[K]C {
	if len(array) == 0 {
		return nil
	}
	arrMap := make(map[K]C, len(array))

	for _, item := range array {
		if filter != nil && filter(item) {
			continue
		}
		key := keyCast(item)
		if _, exist := arrMap[key]; !exist {
			arrMap[key] = valueCast(item)
		}
	}
	return arrMap
}

func SliceAggregation[K comparable, T any](array []T, unique func(T) K) map[K][]T {
	if len(array) == 0 {
		return nil
	}
	arrMap := make(map[K][]T, len(array))

	for _, item := range array {
		key := unique(item)
		if _, exist := arrMap[key]; !exist {
			arrMap[key] = make([]T, 0)
		}
		arrMap[key] = append(arrMap[key], item)
	}
	return arrMap
}

func SliceAggregationAndCount[K comparable, T any](array []T, unique func(T) K) (map[K]int, []K) {
	countMap := make(map[K]int)
	itemList := make([]K, 0)

	for _, item := range array {
		key := unique(item)
		_, exist := countMap[key]
		if exist {
			countMap[key]++
			continue
		}
		countMap[key] = 1
		itemList = append(itemList, key)
	}
	return countMap, itemList
}

func SliceAggregationAndFilterAndCast[K comparable, T, F any](array []T, unique func(T) K, filter func(T) bool, cast func(T) F) map[K][]F {
	if len(array) == 0 {
		return nil
	}
	arrMap := make(map[K][]F, len(array))

	for _, item := range array {
		key := unique(item)
		if filter != nil && filter(item) {
			continue
		}
		if _, exist := arrMap[key]; !exist {
			arrMap[key] = make([]F, 0)
		}
		arrMap[key] = append(arrMap[key], cast(item))
	}
	return arrMap
}

func SliceRevert[T any](itemList []T) []T {
	result := make([]T, 0)
	if len(itemList) == 0 || len(itemList) == 1 {
		return itemList
	}
	for j := len(itemList) - 1; j >= 0; j-- {
		result = append(result, itemList[j])
	}
	return result
}

func ArrayCompare[K comparable, T any](ArrayA, ArrayB []T, keyFunc func(T) K, compare func(a, b T) bool) bool {
	if len(ArrayA) != len(ArrayB) {
		return false
	}

	ArrayAMap := SliceToMap(ArrayA, keyFunc)

	for _, valueB := range ArrayB {
		valueA, exist := ArrayAMap[keyFunc(valueB)]
		if !exist {
			return false
		}

		if !compare(valueA, valueB) {
			return false
		}
	}
	return true
}

func ArrayFindMissValue[K comparable, T, F any](arrayA []T, arrayB []F, castA func(T) K, castB func(F) K) []K {
	mapA := SliceToMap(arrayA, castA)
	result := make([]K, 0)

	for _, b := range arrayB {
		key := castB(b)
		_, exist := mapA[key]
		if !exist {
			result = append(result, key)
		}
	}
	return result
}

func ArrayComplete[Z comparable, T, F any](arrayA []T, arrayB []F, castA func(T) Z, castB func(F) Z, completeA func(T) F) []F {
	if len(arrayA) == len(arrayB) || len(arrayA) == 0 {
		return arrayB
	}

	mapB := SliceToMap(arrayB, castB)
	for _, value := range arrayA {
		if _, exist := mapB[castA(value)]; !exist {
			arrayB = append(arrayB, completeA(value))
		}
	}
	return arrayB
}
